import requests
import json
import time
import pandas as pd
from datetime import datetime
import threading
import traceback
from QFinanceGridModel.base import generate_ut_token  # 确保这个模块可用


class RealTimeStockMonitor:
    def __init__(self, secid):
        """
        初始化实时股票监控器
        :param secid: 股票代码 (格式: 市场.代码, 如 '1.688256')
        """
        self.secid = secid
        self.market, self.code = secid.split('.')

        self.running = False
        self.historical_ticks = []  # 存储历史分笔数据
        self.max_history = 20000  # 最多存储的分笔记录数
        self.data_lock = threading.Lock()  # 线程锁，保护共享数据
        self.last_fetch_time = 0  # 记录上次获取数据的时间
        self.fetch_interval = 3  # 数据获取间隔(秒)

    def __build_eastmoney_stock_url(self):
        """构建东方财富实时数据API URL"""
        base_url = "https://push2.eastmoney.com/api/qt/stock/details/sse"
        fixed_params = {
            "fields1": "f1,f2,f3,f4",  # 请求的一级字段
            "fields2": "f51,f52,f53,f54,f55",  # 请求的二级字段（分笔数据）
            "mpi": "2000",  # 最大推送条目数
            "ut": generate_ut_token(),  # 用户令牌
            "fltt": "2",  # 价格小数位数（2位小数）
            "pos": "-0",  # 数据起始位置
            "wbp2u": "|0|0|0|web"  # 来源标识
        }

        # 合并所有参数
        params = {**fixed_params, "secid": self.secid}

        # 构造URL
        query_string = "&".join(f"{k}={v}" for k, v in params.items())
        return f"{base_url}?{query_string}"

    def __fetch_realtime_stock_data(self):
        """获取实时股票分笔数据"""
        url = self.__build_eastmoney_stock_url()
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
            "Referer": "https://quote.eastmoney.com/"
        }

        try:
            # 使用较短的超时时间，避免阻塞
            response = requests.get(url, headers=headers, timeout=5)
            response.raise_for_status()

            # 直接处理整个响应内容
            content = response.text
            data_blocks = [line for line in content.splitlines() if line.startswith('data:')]

            ticks = []
            for block in data_blocks:
                json_str = block[5:].strip()
                try:
                    data = json.loads(json_str)
                    if data.get("data") and data["data"] is not None:
                        # 处理每个分笔数据
                        for each_ticks in data["data"]["details"]:
                            each_ticks_arr = each_ticks.split(",")
                            if len(each_ticks_arr) >= 5:  # 确保有足够的数据字段
                                each_ticks_dict = {
                                    "time": each_ticks_arr[0],       # 时间
                                    "price": each_ticks_arr[1],      # 价格
                                    "volume": each_ticks_arr[2],     # 成交量
                                    "direction": each_ticks_arr[4]   # 方向
                                }
                                ticks.append(each_ticks_dict)
                except (json.JSONDecodeError, KeyError, IndexError) as e:
                    print(f"[{self.code}] 数据处理错误: {e}")

            return ticks

        except requests.exceptions.RequestException as e:
            print(f"[{self.code}] 请求失败: {e}")
        except Exception as e:
            print(f"[{self.code}] 未知错误: {traceback.format_exc()}")

        return []  # 发生错误时返回空列表

    def start_monitoring(self):
        """启动实时监控"""
        if self.running:
            print(f"[{self.code}] 监控已在运行中")
            return

        print(f"[{self.code}] 启动监控线程")
        self.running = True
        self.monitor_thread = threading.Thread(target=self._monitor_loop, name=f"Monitor-{self.code}")
        self.monitor_thread.daemon = True  # 守护线程，主程序结束时会自动结束
        self.monitor_thread.start()

    def _monitor_loop(self):
        """监控循环的核心逻辑"""
        print(f"[{self.code}] 监控循环启动")
        try:
            while self.running:
                start_time = time.time()

                # 获取数据
                data = self.__fetch_realtime_stock_data()

                if data:
                    # 使用线程锁保护共享数据
                    with self.data_lock:
                        # 追加数据到历史记录
                        self.historical_ticks.extend(data)

                        # 保持历史数据不超过最大值
                        if len(self.historical_ticks) > self.max_history:
                            self.historical_ticks = self.historical_ticks[-self.max_history:]

                    print(f"[{self.code}] 更新 {len(data)} 条数据，总计 {len(self.historical_ticks)} 条")

                # 计算并调整等待时间，确保固定间隔
                elapsed = time.time() - start_time
                sleep_time = max(0, self.fetch_interval - elapsed)
                time.sleep(sleep_time)

        except Exception as e:
            print(f"[{self.code}] 监控循环异常: {traceback.format_exc()}")
        finally:
            print(f"[{self.code}] 监控循环退出")

    def stop_monitoring(self):
        """停止监控"""
        if not self.running:
            return

        print(f"[{self.code}] 正在停止监控...")
        self.running = False
        if hasattr(self, 'monitor_thread') and self.monitor_thread.is_alive():
            self.monitor_thread.join(timeout=2)  # 等待线程结束，最多2秒
            print(f"[{self.code}] 监控已停止")

    def get_realtime_snapshot(self):
        """获取实时数据快照（最新一条数据）"""
        with self.data_lock:  # 使用锁保证线程安全
            if not self.historical_ticks:
                return None

            # 获取最新的一条数据
            latest_tick = self.historical_ticks[-1]
            return {
                '股票代码': self.code,
                '最新价': latest_tick['price'],
                '成交量(手)': latest_tick['volume'],
                '成交方向': latest_tick['direction'],
                '时间': latest_tick['time']
            }

    def save_to_csv(self, filename=None):
        """保存历史数据到CSV"""
        if not filename:
            filename = f"{self.code}_tick_data_{datetime.now().strftime('%Y%m%d%H%M%S')}.csv"

        with self.data_lock:  # 使用锁保证线程安全
            if not self.historical_ticks:
                print(f"[{self.code}] 无数据可保存")
                return None

            df = pd.DataFrame(self.historical_ticks)
            df.to_csv(filename, index=False)

        print(f"[{self.code}] 数据已保存到 {filename}")
        return filename


def main():
    """
    主函数 - 只关注数据获取和打印
    """
    # 示例股票列表
    stock_list = [
        "1.688256",  # 寒武纪
        "1.600519",  # 贵州茅台
        "0.300750",  # 宁德时代
    ]

    print("=" * 60)
    print(f"股票实时监控系统启动 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"监控股票: {', '.join([s.split('.')[1] for s in stock_list])}")
    print("=" * 60)

    # 创建监控器
    monitors = [RealTimeStockMonitor(secid) for secid in stock_list]

    # 启动所有监控线程
    for monitor in monitors:
        monitor.start_monitoring()
        time.sleep(0.1)  # 稍微错开启动时间

    try:
        # 主循环
        while True:
            time.sleep(1)

            # 每5秒打印一次快照
            if int(time.time()) % 5 == 0:
                print("\n" + "=" * 60)
                print(f"{datetime.now().strftime('%H:%M:%S')} 实时数据快照:")

                has_data = False
                for monitor in monitors:
                    snapshot = monitor.get_realtime_snapshot()
                    if snapshot:
                        has_data = True
                        print(f"{snapshot['股票代码']}: 价格={snapshot['最新价']} 方向={snapshot['成交方向']} "
                              f"成交量={snapshot['成交量(手)']}手 时间={snapshot['时间']}")

                if not has_data:
                    print("(暂无数据)")

                print("=" * 60)

    except KeyboardInterrupt:
        print("\n收到停止信号，正在停止所有监控...")
        for monitor in monitors:
            monitor.stop_monitoring()

        # 询问是否保存数据
        save_data = input("是否保存数据到CSV? (y/n): ").lower() == 'y'
        if save_data:
            for monitor in monitors:
                monitor.save_to_csv()

        print("程序已退出")


if __name__ == "__main__":
    # 启动主函数
    main()
